package com.doculibre.constellio.connector.documents;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import com.doculibre.constellio.helper.connector.util.ZipUtils;
import com.google.enterprise.connector.spi.Document;
import com.google.enterprise.connector.spi.Property;
import com.google.enterprise.connector.spi.RepositoryException;
import com.google.enterprise.connector.spi.SpiConstants;
import com.google.enterprise.connector.spi.Value;
import com.google.enterprise.connector.spiimpl.DateValue;

/**
 * A versatile implementation of a document. It is inspired by Google's
 * SmartDocument, except this one allow the call the findProperty method without
 * modifing the state of the document
 * 
 * Also, since the Connector Manager can only receive one content value, this
 * SmartDocument automaticly zip multiple values
 * 
 * @author francisbaril
 * 
 */
public class SmartDocument implements Document {

	private Map<String, SmartProperty> properties = new HashMap<String, SmartProperty>();

	public SmartDocument() {
	}

	public SmartDocument(Map<String, List<Object>> metadata) {
		for (String key : metadata.keySet()) {
			List<Object> values = metadata.get(key);
			put(key, values);
		}
	}

	public void put(String key, Object value) {
		put(key, Arrays.asList(new Object[] { value }));
	}

	public void put(String key, List<?> values) {
		properties.remove(key);
		if (values == null || values.size() == 0) {
			// Don't send it

		} else if (SpiConstants.PROPNAME_CONTENT.equals(key)) {
			try {
				if (values.size() > 1) {
					File zipFile = File.createTempFile("content", ".zip");
					ZipUtils.zip(zipFile, values);
					properties.put(key, new SmartProperty(new FileInputStream(
							zipFile)));
				} else if (values.size() == 1) {
					properties.put(key, new SmartProperty(values.get(0)));
				}
			} catch (IOException e) {
				throw new RuntimeException(e);
			}
		} else if (values.size() == 1) {
			properties.put(key, new SmartProperty(values.get(0)));

		} else {
			properties.put(key, new SmartProperty(values));
		}
	}

	public void delete(String key) {
		properties.remove(key);
	}

	@Override
	// @Deprecated
	// /**
	// * The usage of this method is restricted to Google's Connector Manager,
	// use getProperty instead
	// */
	public final Property findProperty(String name) throws RepositoryException {
		if (properties.get(name) == null) {
			return null;
		}

		SmartProperty protectedProperty = properties.get(name);
		SmartProperty value = new SmartProperty(protectedProperty);
		return value;
	}

	@Override
	public final Set<String> getPropertyNames() throws RepositoryException {
		return properties.keySet();
	}

	@Override
	public String toString() {
		String desc = "<SmartDocument>";

		try {
			for (String propertyName : getPropertyNames()) {
				Property property = findProperty(propertyName);
				desc += "\n\t" + propertyName + "=";
				List<String> values = new ArrayList<String>();
				Value value;
				while ((value = property.nextValue()) != null) {
					values.add(value.toString());
				}
				if (values.size() > 1) {
					for (String strValue : values) {
						desc += "\n\t\t" + strValue;
					}
				} else {
					desc += values.get(0);
				}
			}
		} catch (RepositoryException e) {
		}

		return desc += "\n</SmartDocument>";
	}

	public Value getFirstValue(String propertyName) {
		try {
			Property property = findProperty(propertyName);
			return property == null ? null : property.nextValue();
		} catch (RepositoryException e) {
			throw new RuntimeException(e);
		}
	}

	public String getFirstStringValue(String propertyName) {
		Value value = getFirstValue(propertyName);
		return value == null ? null : value.toString();
	}

	public String getDocID() {
		return getFirstStringValue(SpiConstants.PROPNAME_DOCID);
	}

	public Object getContent() {
		return getFirstStringValue(SpiConstants.PROPNAME_CONTENT);
	}

	public String getContentUrl() {
		return getFirstStringValue(SpiConstants.PROPNAME_CONTENTURL);
	}

	public String getTitle() {
		return getFirstStringValue(SpiConstants.PROPNAME_TITLE);
	}

	public List<Object> asList(Object o) {
		List<Object> list = new ArrayList<Object>();
		list.add(o);
		return list;
	}

	public String getFirstDateValueAsISO8601(String propertyName) {
		return getFirstStringValue(propertyName);
	}

	public Date getFirstDateValueAsDate(String propertyName) {
		Calendar cal = getFirstDateValueAsCalendar(propertyName);
		return cal == null ? null : cal.getTime();
	}

	public Calendar getFirstDateValueAsCalendar(String propertyName) {
		String dateStr = getFirstDateValueAsISO8601(propertyName);
		try {
			return dateStr == null ? null : DateValue
					.iso8601ToCalendar(dateStr);
		} catch (ParseException e) {
			// Not supposed to happen, because the dateStr always provide from
			// the conversion of a calendar
			throw new RuntimeException(e);
		}
	}

	public Calendar getLastModifiedAsCalendar() {
		return getFirstDateValueAsCalendar(SpiConstants.PROPNAME_LASTMODIFIED);
	}
	
	public Date getLastModifiedAsDate() {
		return getFirstDateValueAsDate(SpiConstants.PROPNAME_LASTMODIFIED);
	}

	public String getLastModifiedAsISO8601() {
		return getFirstDateValueAsISO8601(SpiConstants.PROPNAME_LASTMODIFIED);
	}
}
